package com.king.db;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import javax.sql.DataSource;

import org.springframework.jdbc.datasource.DataSourceUtils;

import com.king.App;
import com.king.db.annotation.Column;
import com.king.db.annotation.NColumn;
import com.king.db.annotation.Table;
import com.king.util.Keys;
import com.king.util.ToHumpNaming;
import com.king.util.exception.KingException;
import com.king.util.page.PageCore;

/**
 * 数据库简单工具，获取spring管理的数据库连接直接处理增删改查，不直接处理事务
 * 
 * @author king
 * @date 2025年6月26日-10:12:40
 */
//@Slf4j
public class DBTool {

	private Connection getCon() {
		return DataSourceUtils.getConnection(App.getBean(DataSource.class));
	}

	/**
	 * 插入单表数据
	 * 
	 * @param o
	 * @return
	 * @author king
	 * @throws KingException
	 * @date 2025年7月17日-09:22:22
	 */
	protected int insert(Object o) throws Exception {
		return insertBatch(Arrays.asList(o));
	}

	/**
	 * 批量插入，单条，多行插入方式
	 * 
	 * @param o
	 * @return
	 * @author king
	 * @throws Exception
	 * @date 2025年7月22日-10:08:55
	 */
	protected int insertBatch(List<?> o) throws Exception {
		Map<Integer, Object> columns = new LinkedHashMap<>();
		String insertSql = createInsertSql(o, columns);
		Connection con = getCon();
		PreparedStatement ps = con.prepareStatement(insertSql);
		for (int i = 0; i < columns.size(); i++) {
			// i+1的原因是，SQL的下标是从1开始的，而非0
			ps.setObject(i + 1, columns.get(i));
		}
		return ps.executeUpdate();
	}

	/**
	 * 更新单表数据
	 * 
	 * @param o      要更新的对象
	 * @param values 要更新的字段
	 * @param where  AND条件
	 * @return
	 * @author king
	 * @throws Exception
	 * @date 2025年7月17日-10:23:15
	 */
	protected int update(Object o, String[] values, String where) throws Exception {
		Map<Integer, Object> columns = new LinkedHashMap<>();
		String updateSql = createUpdateSql(o, values, where, columns);
		PreparedStatement ps = getCon().prepareStatement(updateSql);
		for (int i = 0; i < columns.size(); i++) {
			ps.setObject(i + 1, columns.get(i));
		}

		return ps.executeUpdate();
	}

	/**
	 * 查询某表的全部记录条数
	 * 
	 * @param o
	 * @return
	 * @throws Exception
	 * @author king
	 * @date 2025年7月22日-13:42:30
	 */
	protected int count(Object o) throws Exception {
		return count(o, null);
	}

	/**
	 * 查询某表的记录数
	 * 
	 * @param o
	 * @param where 条件
	 * @return
	 * @throws Exception
	 * @author king
	 * @date 2025年7月22日-13:47:11
	 */
	protected int count(Object o, String[] where) throws Exception {
		Table t = o.getClass().getAnnotation(Table.class);
		if (t == null) {
			KingException.build("要插入的数据类须使用@Table注解");
		}
		StringBuilder sql = new StringBuilder();
		sql.append("SELECT COUNT(1) FROM ").append(t.value()).append(" WHERE ");

		Field[] fs = o.getClass().getDeclaredFields();

		NColumn n;
		Column c;
		int sort = 0;
		String name;
		Map<Integer, Object> columns = new LinkedHashMap<>();

		for (Field f : fs) {
			f.setAccessible(true);
			if (Modifier.isStatic(f.getModifiers())) {
				continue;
			}
			n = f.getAnnotation(NColumn.class);
			if (n != null) {
				continue;
			}
			c = f.getAnnotation(Column.class);
			name = f.getName();
			if (c != null) {
				name = c.value();
			}
			if (where != null && in(where, name)) {
				sql.append(name).append("=? AND ");
				columns.put(sort, f.get(o));
				sort++;
			}
		}
		if (columns != null && columns.size() > 0) {
			sql.delete(sql.length() - 4, sql.length());
		}

		PreparedStatement ps = getCon().prepareStatement(sql.toString());

		for (int i = 0; i < columns.size(); i++) {
			ps.setObject(i + 1, columns.get(i));
		}

		ResultSet rs = ps.executeQuery();
		if (rs.next()) {
			return rs.getInt(1);
		}
		return 0;
	}

	/**
	 * 更新
	 * 
	 * @param o      要更新的对象
	 * @param values 要更新的字段，且条件是ID
	 * @return
	 * @throws Exception
	 * @author king
	 * @date 2025年7月17日-11:12:03
	 */
	protected int update(Object o, String[] values) throws Exception {
		return update(o, values, null);
	}

	/**
	 * 更新
	 * 
	 * @param o 要更新的对象，且更新对象的全部对应的字段，条件是ID
	 * @return
	 * @throws Exception
	 * @author king
	 * @date 2025年7月17日-11:11:18
	 */
	protected int update(Object o) throws Exception {
		return update(o, null);
	}

	/**
	 * 查询一条记录，where只有一个字段作为条件时的简单写法
	 * 
	 * @param <T>
	 * @param o
	 * @param values
	 * @param where
	 * @return
	 * @throws Exception
	 */
	protected <T> T selectOnee(T o, String[] values, String where) throws Exception {
		return selectOnee(o, values, where, null);
	}

	/**
	 * 查询一条记录，where只有一个条件时的简单写法
	 * 
	 * @param <T>
	 * @param o
	 * @param values
	 * @param where
	 * @param sort
	 * @return
	 * @throws Exception
	 */
	protected <T> T selectOnee(T o, String[] values, String where, String sort) throws Exception {
		return selectOne(o, values, new String[] { where }, sort);
	}

	/**
	 * 查询一条记录的所有字段，条件是ID
	 * 
	 * @param <T>
	 * @param o
	 * @return
	 * @throws Exception
	 * @author king
	 * @date 2025年7月18日-17:44:41
	 */
	protected <T> T selectOne(T o) throws Exception {
		return selectOne(o, null);
	}

	/**
	 * 查询一条记录，指定字段，条件是ID
	 * 
	 * @param <T>
	 * @param o
	 * @param values
	 * @return
	 * @throws Exception
	 * @author king
	 * @date 2025年7月18日-17:44:25
	 */
	protected <T> T selectOne(T o, String[] values) throws Exception {
		return selectOnee(o, values, "id");
	}

	/**
	 * 查询一条记录
	 * 
	 * @param <T>
	 * @param o
	 * @param values
	 * @param where
	 * @return
	 * @throws Exception
	 */
	protected <T> T selectOne(T o, String[] values, String[] where) throws Exception {
		return selectOne(o, values, where, null);
	}

	/**
	 * 查询一个对象
	 * 
	 * @param <T>
	 * @param o
	 * @param clz
	 * @param values
	 * @param where
	 * @param sort   已包含了order by，只需要填写字段及排序方式即可，如：create_date DESC
	 * @return
	 * @author king
	 * @throws Exception
	 * @date 2025年7月17日-16:27:56
	 */
	protected <T> T selectOne(T o, String[] values, String[] where, String sort) throws Exception {
		List<T> r = select(o, values, where, sort, true);
		if (r != null && r.size() != 0) {
			return r.getFirst();
		}
		return null;
	}

	protected <T> List<T> select(T o) throws Exception {
		return select(o, null);
	}

	protected <T> List<T> select(T o, String[] values) throws Exception {
		return select(o, values, null);
	}

	protected <T> List<T> select(T o, String[] values, String[] where) throws Exception {
		return select(o, values, where, null);
	}

	/**
	 * 查询一个集合出来
	 * 
	 * @param <T>
	 * @param o
	 * @param values
	 * @param where
	 * @param sort
	 * @return
	 * @throws Exception
	 * @author king
	 * @date 2025年7月22日-17:40:07
	 */
	protected <T> List<T> select(T o, String[] values, String[] where, String sort) throws Exception {
		return select(o, values, where, sort, false);
	}

	/**
	 * 查询记录数
	 * 
	 * @param <T>
	 * @param o
	 * @param values
	 * @param where
	 * @param sort
	 * @param oneIs  是否只查询一条，true:是：false：否
	 * @return
	 * @author king
	 * @throws Exception
	 * @date 2025年7月22日-17:32:46
	 */
	@SuppressWarnings("unchecked")
	protected <T> List<T> select(T o, String[] values, String[] where, String sort, boolean oneIs) throws Exception {
		Map<Integer, Object> columns = new LinkedHashMap<>();
		List<String> tmpValues;
		// 处理当要查询的values是空时，那么创建SQL时，就直接将字段全部带出来
		if (values != null) {
			tmpValues = new ArrayList<>(Arrays.asList(values));
		} else {
			tmpValues = new ArrayList<>();
		}

		String sql = createSelectSql(o, tmpValues, where, sort, columns, oneIs ? 1 : 0);

		values = tmpValues.toArray(new String[0]);

		PreparedStatement ps = getCon().prepareStatement(sql);
		for (int i = 0; i < columns.size(); i++) {
			ps.setObject(i + 1, columns.get(i));
		}
		ResultSet rs = ps.executeQuery();
		Field f;
		T instance = null;
		List<T> r = new ArrayList<>();

		while (rs.next()) {
			instance = (T) o.getClass().getDeclaredConstructor().newInstance();
			for (String v : values) {
				if (v.indexOf("_") >= 0) {
					f = instance.getClass().getDeclaredField(ToHumpNaming.toHump(v));
				} else {
					f = instance.getClass().getDeclaredField(v);
				}
				if (f != null) {
					f.setAccessible(true);
					if (f.getType().equals(LocalDateTime.class)) {
						String datev = rs.getString(v);
						if (datev != null) {
							// 有时候，返回的日期时间中间是空格，有时候是T，这里将统一格式化，将空格的换成T
							datev = datev.replaceFirst("\s", "T");
							f.set(instance, LocalDateTime.parse(datev));
						} else {
							f.set(instance, null);
						}
					} else {
						f.set(instance, rs.getObject(v));
					}
				}
			}
			r.add(instance);
			if (oneIs) {
				break;
			}
		}
		return r;
	}

	/**
	 * 
	 * @param o
	 * @param values
	 * @param where
	 * @param sort
	 * @param columns
	 * @param limit   限制条数
	 * @return
	 * @throws Exception
	 * @author king
	 * @date 2025年7月17日-16:43:32
	 */
	private String createSelectSql(Object o, List<String> values, String[] where, String sort,
			Map<Integer, Object> columns,
			int limit)
			throws Exception {
		Table t = o.getClass().getAnnotation(Table.class);
		if (t == null) {
			KingException.build("要插入的数据类须使用@Table注解");
		}

		Field[] fs = o.getClass().getDeclaredFields();
		StringBuilder sb = new StringBuilder();
		sb.append("SELECT ");
		Column c;
		NColumn nc;
		String name;

		List<String> tmpVals = new ArrayList<>();

		Map<String, Object> wo = new HashMap<>();
		for (Field f : fs) {
			f.setAccessible(true);
			if (Modifier.isStatic(f.getModifiers())) {
				continue;
			}
			nc = f.getAnnotation(NColumn.class);
			if (nc != null) {
				continue;
			}
			c = f.getAnnotation(Column.class);
			name = f.getName();
			if (c != null) {
				name = c.value();
			}
			if (where != null && in(where, name)) {
				wo.put(name, f.get(o));
			}
			if (values.size() > 0 && values.contains(name)) {
				sb.append(name).append(",");
			} else {
				sb.append(name).append(",");
				tmpVals.add(name);
			}
		}
		
		if (values.size() == 0) {
			values.addAll(tmpVals);
		}

		sb.delete(sb.length() - 1, sb.length());
		sb.append(" FROM ").append(t.value());
		if (where != null) {
			int sortNum = 0;
			sb.append(" WHERE ");
			for (String k : wo.keySet()) {
				sb.append(k).append("=? AND ");
				columns.put(sortNum, wo.get(k));
				sortNum++;
			}
			sb.delete(sb.length() - 4, sb.length());
		}
		if (sort != null) {
			sb.append(" ORDER BY ").append(sort);
		}
		if (limit != 0) {
			sb.append(" LIMIT ").append(limit);
		}
		return sb.toString();
	}

	/**
	 * 创建更新SQL
	 * 
	 * @param o       要更新的对象
	 * @param values  要更新的字段，同数据库字段名
	 * @param where   AND条件，目前仅支持一个
	 * @param columns 收集的顺序和值
	 * @return
	 * @throws Exception
	 * @author king
	 * @date 2025年7月17日-11:09:48
	 */
	private String createUpdateSql(Object o, String[] values, String where, Map<Integer, Object> columns)
			throws Exception {
		Table t = o.getClass().getAnnotation(Table.class);
		if (t == null) {
			KingException.build("要插入的数据类须使用@Table注解");
		}

		if (where == null) {
			where = "id";
		}

		Field[] fs = o.getClass().getDeclaredFields();
		StringBuilder sb = new StringBuilder();
		sb.append("UPDATE ").append(t.value()).append(" SET ");
		int sort = 0;
		String name;
		Object wo = null;
		Column c;
		NColumn nc;
		for (Field f : fs) {
			f.setAccessible(true);
			if (Modifier.isStatic(f.getModifiers())) {
				continue;
			}
			nc = f.getAnnotation(NColumn.class);

			if (nc != null) {
				continue;
			}
			c = f.getAnnotation(Column.class);
			name = f.getName();
			if (c != null) {
				name = c.value();
			}

			if (values != null) {
				if (in(values, name)) {
					columns.put(sort, f.get(o));
					sb.append(name).append("=?,");
					sort++;
				}
			} else {
				columns.put(sort, f.get(o));
				sb.append(name).append("=?,");
				sort++;
			}
			if (name.equals(where)) {
				wo = f.get(o);
			}
		}

		columns.put(sort, wo);

		sb.delete(sb.length() - 1, sb.length());

		sb.append(" where ").append(where).append("=?");

		return sb.toString();
	}

	private boolean in(String[] vals, String val) {
		for (String v : vals) {
			if (val.equals(v)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 创建插入语句，这里包含了多行，单条插入语句，适用于小数据量的，此处封装也仅是小数据量（几条，十几条）所以不使用JDBC的addBatch了
	 * 
	 * @param o       要插入的对象
	 * @param columns 收集插入值及序号
	 * @return
	 * @throws Exception
	 * @author king
	 * @date 2025年7月17日-09:48:47
	 */
	private String createInsertSql(List<?> o, Map<Integer, Object> columns) throws Exception {
		// 反射，获取参数的字段并生成insert SQL
		Object first = o.getFirst();
		Table t = first.getClass().getAnnotation(Table.class);
		if (t == null) {
			KingException.build("要插入的数据类须使用@Table注解");
		}
		Field[] fs = first.getClass().getDeclaredFields();
		StringBuilder sb = new StringBuilder();
		// insert into tableName(id,name...) value(?,?,?...);
		sb.append("INSERT INTO ").append(t.value()).append("(");
		Column c;
		NColumn n;
		int sort = 0;
		for (Field f : fs) {
			f.setAccessible(true);
			if (Modifier.isStatic(f.getModifiers())) {
				continue;
			}
			n = f.getAnnotation(NColumn.class);
			if (n != null) {
				continue;
			}
			c = f.getAnnotation(Column.class);
			if (c == null) {
				sb.append(f.getName()).append(",");
				columns.put(sort, f.get(first));
			} else {
				sb.append(c.value()).append(",");
				columns.put(sort, f.get(first));
			}
			sort++;
		}
		sb.delete(sb.length() - 1, sb.length());
		sb.append(")VALUES");
		StringBuilder val = new StringBuilder();
		val.append("(");
		for (int i = 0; i < columns.size(); i++) {
			if (i != columns.size() - 1) {
				val.append("?").append(",");
			} else {
				val.append("?)");
			}
		}
		sb.append(val);
		if (o.size() > 1) {
			StringBuilder values = new StringBuilder();
			// 如果批量插入的数据超过一个，那么后面的继续进行反射获取数据
			List<Object> oo = new ArrayList<>(o);
			// 移除第一个，因为在上面的代码中已经处理了
			oo.remove(o.getFirst());
			for (Object ooo : oo) {
				// 拼接成(?,?...),(?,?...)的格式
				values.append(",").append(val);
				fs = ooo.getClass().getDeclaredFields();
				for (Field f : fs) {
					f.setAccessible(true);
					if (Modifier.isStatic(f.getModifiers())) {
						continue;
					}
					n = f.getAnnotation(NColumn.class);
					if (n != null) {
						continue;
					}
					c = f.getAnnotation(Column.class);
					if (c == null) {
						columns.put(sort, f.get(ooo));
					} else {
						columns.put(sort, f.get(ooo));
					}
					sort++;
				}
			}
			sb.append(values);
		}
		return sb.toString();
	}

	/**
	 * 删除数据
	 * 
	 * @param o
	 * @param where
	 * @return
	 * @author king
	 * @throws Exception
	 * @date 2025年7月22日-11:37:47
	 */
	protected int delete(Object o, String[] where) throws Exception {
		Objects.requireNonNull(where, "where条件不能为空");
		Table t = o.getClass().getAnnotation(Table.class);
		if (t == null) {
			KingException.build("要插入的数据类须使用@Table注解");
		}
		StringBuilder sql = new StringBuilder();
		sql.append("DELETE FROM ").append(t.value()).append(" WHERE ");
		Field[] fs = o.getClass().getDeclaredFields();
		Column c;
		NColumn n;
		String name;
		int sort = 0;
		Map<Integer, Object> columnVal = new LinkedHashMap<>();
		for (Field f : fs) {
			f.setAccessible(true);
			if (Modifier.isStatic(f.getModifiers())) {
				continue;
			}
			n = f.getAnnotation(NColumn.class);
			if (n != null) {
				continue;
			}
			c = f.getAnnotation(Column.class);
			name = f.getName();
			if (c != null) {
				name = c.value();
			}
			if (in(where, name)) {
				sql.append(name).append("=? AND ");
				columnVal.put(sort, f.get(o));
				sort++;
			}
		}
		sql.delete(sql.length() - 4, sql.length());
		PreparedStatement ps = getCon().prepareStatement(sql.toString());
		for (int i = 0; i < columnVal.size(); i++) {
			ps.setObject(i + 1, columnVal.get(i));
		}

		return ps.executeUpdate();
	}

	/**
	 * 通过ID删除数据
	 * 
	 * @param o
	 * @return
	 * @author king
	 * @throws Exception
	 * @date 2025年7月22日-11:38:13
	 */
	protected int delete(Object o) throws Exception {
		return delete(o, new String[] { "id" });
	}

	/**
	 * 分页
	 * 
	 * @param search
	 * @param dao
	 * @return
	 * @author king
	 * @date 2025年6月26日-10:18:43
	 */
	public List<Map<String, Object>> page(PageCore search, TopDao dao) {
		search.setRows(dao.selPageCount(search));
		if (search.getRows() == 0) {
			return Keys.LIST_OBJ_MAP;
		}
		return dao.selPageList(search);
	}
}
